//
// Created on 2024/8/1.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef NATIVEPJSIP_PJSIP_MANAGER_H
#define NATIVEPJSIP_PJSIP_MANAGER_H
#include <pjsua2.hpp>
#include <iostream>
#include <string.h>
#include "my_call.hpp"
#include "my_account.hpp"
#include "my_loger_writer.hpp"
#include "my_audio_media_port.hpp"
#include "napi/native_api.h"

using namespace std;

struct CallbackData {
    napi_ref callbackRef = nullptr;
    napi_ref stateCallbackRef = nullptr;
    napi_ref recordCallbackRef = nullptr;
    napi_ref incomingCallbackRef = nullptr;
    napi_ref qualityCallbackRef = nullptr;
    napi_threadsafe_function tsfn;
    napi_env env;
};

struct pjsip_result
{
    bool success;
    string error_msg;
    string call_id;
};

class PJSUAManager
{

private:
    
    static PJSUAManager *_instance;
    
    pj::Endpoint _mEndpoint;

    MyCall *_currentCall = nullptr;
    MyAccount *_myAccount = nullptr;
    MyLogWriter *_mLogWriter = nullptr;
    MyAudioMediaPort *_med_port = nullptr;
    MyAudioMediaPort* _remote_med_port = nullptr;

    int _uniqueId = 0;

    AudioMediaPlayer *startRingPlayer;
    AudioMediaPlayer *endRingPlayer;
    AudioMediaPlayer *incomingRingPlayer;
    AudioMediaRecorder *_check_wav_writer = nullptr;
    pj_timer_entry m_check_timer{};
    pj_timer_entry m_update_timer{};

    string _username = "";
    string _password = "";
    string _ipAddress = "";
    bool _sdkInstanced = false;
    bool _isLogined = false;
    bool _isHangupSelf = false;
    bool _isStartUpdaterRegister = false;
    bool _isMute = false;
    pjsip_inv_state _last_call_state;
    std::map<string, pj_uint8_t> _codecPriorities;

    int uniqueId = 0;
    int lastRxLoss = 0;
    int lastRxPkt = 0;
    int badTimes = 0;
    int goodTimes = 0;
    int badPktTimes = 0;
    string currentNetworkStatus = "";

    unsigned _currentPort;

    ~PJSUAManager(){};
    PJSUAManager(const PJSUAManager &);
    PJSUAManager &operator=(const PJSUAManager &);

    void sendFlutterStateChnage(string state_text, string callId);
    void sendFlutterIncomingCall(string callId, string xid, string remoteUri);
    void startChecker();
    void startUpdateResgisterChecker();
    void rx_checker_start(int msec);
    void register_checker_start(int msec);
    static void rx_check_callback(pj_timer_heap_t *timer_heap,
                                  struct pj_timer_entry *entry);
    static void register_check_callback(pj_timer_heap_t* timer_heap,
                                        struct pj_timer_entry* entry);
    void watchNetwork();
    void updateRegister();
    void monitorStopAudio(CallInfo callInfo);
    void monitorNetworkHealth(CallInfo callInfo);
    void updateNetQuality(string quality, int loss);

    string current_working_directory();

public:
    CallbackData *callbackData;
    napi_threadsafe_function hangup_tsfn;
    pjsua_player_id m_player_id{PJSUA_INVALID_ID};
    pjsua_player_id m_end_player_id{PJSUA_INVALID_ID};

    PJSUAManager(){};
    
    static PJSUAManager *getInstace()
    {
        if (_instance == nullptr)
        {
            _instance = new PJSUAManager();
            _instance->_currentPort = 6050;
        }
        return _instance;
    }
    
    bool InitSDK(bool enableUA, bool enableStun, int uniqueId_it, CallbackData *callbackData);
    bool CreateWav(string ringPath, string hangupPath, string incomingPath);
    bool DeInitSDK();
    bool RegisterAccount(string username, string password, string ip, string port);
    bool UnregisterAccount();
    bool Recive();
    bool Mute();
    pjsip_result MakeCall(string number, string displayNumber, string wavPath, map<string, string> xHeaders);
    bool HangupCall();
    void SendDTMF(string value);
    
    void StopHangupRing();
    void StopPlayer();
    void StartRecord();
    void StopRecord();
    bool StartRecordRemote();
    void StopRecordRemote();
    bool CheckLoopAudio(string recordPath);
    bool StopCheckLoopAudio();
    void UpdateCodecWithOnlyG711(bool useOnlyG711);

    void onImcomingCall(int callId, string xid);
    void onCallStateChange(CallInfo info);
    void adjustVoidLevel(float level);


    void sendFlutterRecordBuffer(string method, ByteVector buffer);
};

#endif //NATIVEPJSIP_PJSIP_MANAGER_H
